#include "pd_core.h"
#include "tcpci_event.h"
#include "tcpci.h"
#include "tcpci_core.h"
#include "tcpci_timer.h"
#include "tcpm.h"
#include "tcpm_pd.h"
#include "pd_dpm_core.h"
#include "pd_policy_engine.h"

#undef LOG_TAG 
#define LOG_TAG    "pd_core"
#include "elog.h"

static inline void pd_parse_pdata_mfrs(struct pd_port *pd_port)
{
	uint32_t vid = MANUFACTURER_INFO_VID_PORT;
	uint32_t pid = MANUFACTURER_INFO_PID_PORT;
#if CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
	const char *mstring = "SgmicroTCPC";
	struct pd_manufacturer_info *mfrs_info = &pd_port->mfrs_info;

	log_i("%s VID = 0x%x, PID = 0x%x\n", __func__, vid, pid);
	mfrs_info->vid = vid;
	mfrs_info->pid = pid;

	memcpy(&mfrs_info->mfrs_string, mstring, strlen(mstring));
	log_i("PD mfrs_string = %s\n", mfrs_info->mfrs_string);
#endif	/* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
}

static inline void pd_parse_pdata_bat_info(struct pd_port *pd_port,
	struct pd_battery_info *bat_info)
{
	uint32_t design_cap;
	uint32_t vid = BAT_VID, pid = BAT_PID;
#if CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
	const char *mstring = {"bat1"};
	struct pd_manufacturer_info *mfrs_info = &bat_info->mfrs_info;
#endif	/* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */
	struct pd_battery_capabilities *bat_cap = &bat_info->bat_cap;

#if CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL
	mfrs_info->vid = vid;
	mfrs_info->pid = pid;
	memcpy(&mfrs_info->mfrs_string, mstring, strlen(mstring));
#endif	/* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */

	design_cap = 4000;
	bat_cap->bat_design_cap = (uint16_t)PD_BCDB_BAT_CAP_RAW(design_cap);
	bat_cap->vid = vid;
	bat_cap->pid = pid;
	bat_cap->bat_last_full_cap = PD_BCDB_BAT_CAP_UNKNOWN;
	bat_info->bat_status = (uint32_t)BSDO((uint32_t)BSDO_BAT_CAP_UNKNOWN, BSDO_BAT_INFO_IDLE);
}

struct pd_battery_info g_fix_bat_info[NUM_FIXED_BATTERIES];
static inline void pd_parse_pdata_bats(struct pd_port *pd_port)
{
	int i;

	pd_port->bat_nr = NUM_FIXED_BATTERIES;
	log_i("%s Battery NR = %d\n", __func__, pd_port->bat_nr);

	pd_port->fix_bat_info = g_fix_bat_info;
	for (i = 0; i < pd_port->bat_nr; i++) {
		pd_parse_pdata_bat_info(pd_port,&pd_port->fix_bat_info[i]);

		log_i("fix_bat_info[%d].mfrs_info.vid = 0x%x, "
			".mfrs_info.pid = 0x%x, .mfrs_string = %s, "
			".bat_design_cap = %d\n", i,
			pd_port->fix_bat_info[i].mfrs_info.vid,
			pd_port->fix_bat_info[i].mfrs_info.pid,
			pd_port->fix_bat_info[i].mfrs_info.mfrs_string,
			PD_BCDB_BAT_CAP_VAL(pd_port->fix_bat_info[i].bat_cap.bat_design_cap));
	}
}

#if CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY
struct pd_country_authority g_country_info;
static inline void __attribute__((unused)) pd_parse_pdata_countries(struct pd_port *pd_port)
{
    uint8_t i, j;

    pd_port->country_nr = 1;
    pd_port->country_info = &g_country_info;

    for (i = 0; i < pd_port->country_nr; i++) {
        pd_port->country_info[i].len = 2;
        pd_port->country_info[i].code = 0x434E;  // CN
        for (j = 0; j < pd_port->country_info[i].len; j++)
            pd_port->country_info->data[j] = 0xFF;
    }
}
#endif /* CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY */

static inline void pd_parse_pdata_src_cap_ext(struct pd_port *pd_port)
{
#if CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL
	pd_port->src_cap_ext.vid = USB_VID_SOP;
	pd_port->src_cap_ext.pid = PID_SOP;
	pd_port->src_cap_ext.xid = XID_SOP;
	pd_port->src_cap_ext.fw_ver = 0;
	pd_port->src_cap_ext.hw_ver = 0;
	pd_port->src_cap_ext.voltage_regulation = 0;
	pd_port->src_cap_ext.hold_time_ms = 0;
	pd_port->src_cap_ext.compliance = 0;
	pd_port->src_cap_ext.touch_current = 0;
	pd_port->src_cap_ext.peak_current[0] =0 ;
	pd_port->src_cap_ext.peak_current[1] =0 ;
	pd_port->src_cap_ext.peak_current[2] =0 ;
	pd_port->src_cap_ext.touch_temp = 0;
	pd_port->src_cap_ext.source_inputs = 0;
	pd_port->src_cap_ext.batteries = 0;
	pd_port->src_cap_ext.source_pdp = PD_POWER_AS_SOURCE / 1000;
	pd_port->src_cap_ext.epr_source_pdp = 0;
#if CONFIG_USB_PD_REV30_BAT_INFO
	pd_port->src_cap_ext.batteries = PD_SCEDB_BATTERIES(
	        NUM_SWAPPABLE_BATTERY_SLOTS, pd_port->bat_nr);
	if (pd_port->src_cap_ext.batteries)
		pd_port->src_cap_ext.source_inputs |= PD_SCEDB_INPUT_INT;
#endif	/* CONFIG_USB_PD_REV30_BAT_INFO */
#endif	/* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */
}

static inline void pd_parse_pdata_snk_cap_ext(
	struct pd_port *pd_port)
{
#if CONFIG_USB_PD_REV30
	pd_port->snk_cap_ext.vid = USB_VID_SOP;
	pd_port->snk_cap_ext.pid = PID_SOP;
	pd_port->snk_cap_ext.xid = XID_SOP;
	pd_port->snk_cap_ext.fw_ver = 0;
	pd_port->snk_cap_ext.hw_ver = 0;
	pd_port->snk_cap_ext.skedb_ver = 0;
	pd_port->snk_cap_ext.load_step = 0;
	pd_port->snk_cap_ext.load_char = 0;
	pd_port->snk_cap_ext.compliance = 0;
	pd_port->snk_cap_ext.touch_temp = 0;
	pd_port->snk_cap_ext.battery_info = 0;
	pd_port->snk_cap_ext.sink_modes = 1 << 3;  // Battery Powered
	pd_port->snk_cap_ext.min_pdp = 0;
	pd_port->snk_cap_ext.oper_pdp = 0;
	pd_port->snk_cap_ext.max_pdp = PD_POWER_AS_SINK / 1000;
	pd_port->snk_cap_ext.epr_min_pdp = 0;
	pd_port->snk_cap_ext.epr_oper_pdp = 0;
	pd_port->snk_cap_ext.epr_max_pdp = 0;
#if CONFIG_USB_PD_REV30_BAT_INFO
	pd_port->snk_cap_ext.battery_info = PD_SKEDB_BATTERIES
	        (NUM_SWAPPABLE_BATTERY_SLOTS, pd_port->bat_nr);
#endif	/* CONFIG_USB_PD_REV30_BAT_INFO */
#endif	/* CONFIG_USB_PD_REV30 */
}

static int pd_parse_pdata(struct pd_port *pd_port)
{
    uint32_t src_fix_pdo_flag = 0, snk_fix_pdo_flag = 0;

    // Source Capabilities PDO
    pd_port->local_src_cap_default.nr = NUM_SRC_PDOS;
    src_fix_pdo_flag |= PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP;
    src_fix_pdo_flag |= (uint32_t)UNCONSTRAINED_POWER << 27 | \
                        (uint32_t)USB_COMMS_CAPABLE << 26 | \
                        (uint32_t)UNCHUNKED_EXTENDED_MESSAGES_SUPPORTED << 24 | \
                        (uint32_t)EPR_SUPPORTED_AS_SRC << 23;
    pd_port->local_src_cap_default.pdos[0] = PDO_FIXED(SRC_PDO_VOLTAGE, SRC_PDO_MAX_CURRENT,
                                   SRC_PDO_PEAK_CURRENT, src_fix_pdo_flag) | PDO_TYPE_FIXED;

    // Sink Capabilities PDO
    pd_port->local_snk_cap.nr = NUM_SNK_PDOS;
    snk_fix_pdo_flag |= PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP;
    snk_fix_pdo_flag |= (uint32_t)UNCONSTRAINED_POWER << 27 | \
                        (uint32_t)USB_COMMS_CAPABLE << 26 | \
                        ((uint32_t)FR_SWAP_REQD_TYPE_C_CURRENT_AS_INITIAL_SOURCE & 0x3) << 23;
    pd_port->local_snk_cap.pdos[0] = PDO_FIXED(SNK_PDO0_VOLTAGE, SNK_PDO0_OP_CURRENT,
                                               0, src_fix_pdo_flag) | PDO_TYPE_FIXED;
#if NUM_SNK_PDOS == 2
    pd_port->local_snk_cap.pdos[1] = APDO_PPS(SNK_PDO1_MIN_VOLTAGE, SNK_PDO1_MAX_VOLTAGE,
                                                 SNK_PDO1_OP_CURRENT);
#if CONFIG_USB_PD_REV30_PPS_SINK
    pd_port->local_snk_cap_nr_pd20 = 1; // PD2.0 do not support PPS
    pd_port->local_snk_cap_nr_pd30 = pd_port->local_snk_cap.nr;
#endif	/* CONFIG_USB_PD_REV30_PPS_SINK */
#endif

    /*
     * SVDM Discover Identity ACK
     * id_vdos[0] - ID Header VDO
     * id_vdos[1] - Cert Stat VDO
     * id_vdos[2] - Product VDO
     * id_vdo2[3] - DFP VDO
     */
    pd_port->id_vdo_nr = 4;
    pd_port->id_vdos[0] = VDO_IDH(TYPE_C_CAN_ACT_AS_HOST, TYPE_C_CAN_ACT_AS_DEVICE,
                                  PRODUCT_TYPE_UFP_SOP, MODAL_OPERATION_SUPPORTED_SOP,
                                  PRODUCT_TYPE_DFP_SOP, ID_HEADER_CONNECT_TYPE_SOP,
                                  USB_VID_SOP);
    pd_port->id_vdos[1] = (uint32_t)XID_SOP;
    pd_port->id_vdos[2] = VDO_PRODUCT(PID_SOP, BCDDEVICE_SOP);
    pd_port->id_vdos[3] = VDO_DFP(2, HOST_CAPABILITY, DFP_VDO_PORT_NUMBER);

#if CONFIG_USB_PD_REV30
	pd_port->id_header = pd_port->id_vdos[0];
#endif	/* CONFIG_USB_PD_REV30 */

	pd_port->dpm_charging_policy = DPM_CHARGING_POLICY_MAX_POWER | \
	                               DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR | \
	                               DPM_CHARGING_POLICY_PREFER_LOW_VOLTAGE;
	pd_port->dpm_charging_policy_default = pd_port->dpm_charging_policy;
#if CONFIG_USB_PD_REV30_PPS_SINK
    // set initial PPS request, may modified by tcpm later
    pd_port->request_v_apdo = SNK_PDO1_MIN_VOLTAGE * 100;
    pd_port->request_i_apdo = SNK_PDO1_OP_CURRENT * 50;
#endif

#if CONFIG_USB_PD_REV30_BAT_INFO
	pd_parse_pdata_bats(pd_port);
#endif	/* CONFIG_USB_PD_REV30_BAT_INFO */

#if CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY
    // pd_parse_pdata_countries(pd_port);
#endif	/* CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY */

	pd_parse_pdata_src_cap_ext(pd_port);
	pd_parse_pdata_snk_cap_ext(pd_port);
	pd_parse_pdata_mfrs(pd_port);
	return 0;
}

void pd_notify_pe_transit_to_default(struct pd_port *pd_port)
{
	struct sgm41620_tcpc *tcpc = p_tcpc_dev;

	pd_update_connect_state(pd_port, PD_CONNECT_HARD_RESET);

	tcpc->pd_hard_reset_event_pending = false;
	tcpc->pd_wait_pr_swap_complete = false;
	tcpc->pd_bist_mode = PD_BIST_MODE_DISABLE;

#if CONFIG_USB_PD_DIRECT_CHARGE
	tcpc->pd_during_direct_charge = false;
#endif	/* CONFIG_USB_PD_DIRECT_CHARGE */

}

#define PE_RESET_MSG_ID(pd_port, sop)	{ \
	pd_port->pe_data.msg_id_tx[sop] = 0; \
	pd_port->pe_data.msg_id_rx[sop] = PD_MSG_ID_MAX; \
}

//dpm_caps dts
static const struct {
#if DPM_CAP_INFO
	const char *prop_name;
	const char *legacy_prop_name;
#endif
	uint32_t val;
} supported_dpm_caps[] = {
#if DPM_CAP_INFO
	{"local-dr-power", "local_dr_power", DPM_CAP_LOCAL_DR_POWER},
	{"local-dr-data", "local_dr_data", DPM_CAP_LOCAL_DR_DATA},
#if UNCONSTRAINED_POWER
	{"local-ext-power", "local_ext_power", DPM_CAP_LOCAL_EXT_POWER},
#endif
#if USB_COMMS_CAPABLE
	{"local-usb-comm", "local_usb_comm", DPM_CAP_LOCAL_USB_COMM},
#endif
	//{"local-usb-suspend", "local_use_suspend", DPM_CAP_LOCAL_USB_SUSPEND},
	//{"local-high-cap", "local_high_cap", DPM_CAP_LOCAL_HIGH_CAP},
	//{"local-give-back", "local_give_back", DPM_CAP_LOCAL_GIVE_BACK},
	{"local-no-suspend", "local_no_suspend", DPM_CAP_LOCAL_NO_SUSPEND},

#if CONFIG_PD_ALT_MODE_SUPPORT
	{"attempt-enter-dp-mode", "attempt_enter_dp_mode", DPM_CAP_ATTEMPT_ENTER_DP_MODE},
#endif
	{"attempt-discover-cable", "attempt_discover_cable", DPM_CAP_ATTEMPT_DISCOVER_CABLE},
	{"attempt-discover-id", "attempt_discover_id", DPM_CAP_ATTEMPT_DISCOVER_ID},
	//{"attempt-discover-id-dfp", "attempt_discover_id_dfp", DPM_CAP_ATTEMPT_DISCOVER_ID_DFP},
	//{"attempt-discover-svids", "attempt_discover_svids", DPM_CAP_ATTEMPT_DISCOVER_SVIDS},

	//{"pr-reject-as-source", "pr_reject_as_source", DPM_CAP_PR_SWAP_REJECT_AS_SRC},
	//{"pr-reject-as-sink", "pr_reject_as_sink", DPM_CAP_PR_SWAP_REJECT_AS_SNK},
	//{"pr-check-gp-source", "pr_check_gp_source", DPM_CAP_PR_SWAP_CHECK_GP_SRC},
	//{"pr-check-gp-sink", "pr_check_gp_sink", DPM_CAP_PR_SWAP_CHECK_GP_SNK},

	//{"dr-reject-as-dfp", "dr_reject_as_dfp", DPM_CAP_DR_SWAP_REJECT_AS_DFP},
	//{"dr-reject-as-ufp", "dr_reject_as_ufp", DPM_CAP_DR_SWAP_REJECT_AS_UFP},

	//{"dp-prefer-mf", "dp_prefer_mf", DPM_CAP_DP_PREFER_MF},
};
#else
	{DPM_CAP_LOCAL_DR_POWER},
	{DPM_CAP_LOCAL_DR_DATA},
#if UNCONSTRAINED_POWER
	{DPM_CAP_LOCAL_EXT_POWER},
#endif
#if USB_COMMS_CAPABLE
	{DPM_CAP_LOCAL_USB_COMM},
#endif
	//{DPM_CAP_LOCAL_USB_SUSPEND},
	//{DPM_CAP_LOCAL_HIGH_CAP},
	//{DPM_CAP_LOCAL_GIVE_BACK},
	{DPM_CAP_LOCAL_NO_SUSPEND},

#if CONFIG_PD_ALT_MODE_SUPPORT
	{DPM_CAP_ATTEMPT_ENTER_DP_MODE},
#endif
	{DPM_CAP_ATTEMPT_DISCOVER_CABLE},
	{DPM_CAP_ATTEMPT_DISCOVER_ID},
	//{DPM_CAP_ATTEMPT_DISCOVER_ID_DFP},
	//{DPM_CAP_ATTEMPT_DISCOVER_SVIDS},

	//{DPM_CAP_PR_SWAP_REJECT_AS_SRC},
	//{DPM_CAP_PR_SWAP_REJECT_AS_SNK},
	//{DPM_CAP_PR_SWAP_CHECK_GP_SRC},
	//{DPM_CAP_PR_SWAP_CHECK_GP_SNK},

	//{DPM_CAP_DR_SWAP_REJECT_AS_DFP},
	//{DPM_CAP_DR_SWAP_REJECT_AS_UFP},

	//{DPM_CAP_DP_PREFER_MF},
};
#endif

static void pd_core_power_flags_init(struct pd_port *pd_port)
{
    uint32_t src_flag, snk_flag;
    struct pd_port_power_caps *snk_cap = &pd_port->local_snk_cap;
    struct pd_port_power_caps *src_cap = &pd_port->local_src_cap_default;
    int i;

    for (i = 0; i < ARRAY_SIZE(supported_dpm_caps); i++) {
        pd_port->dpm_caps |= supported_dpm_caps[i].val;
#if DPM_CAP_INFO
        printf("dpm_caps: %s\n", supported_dpm_caps[i].prop_name);
#endif
    }

    pd_port->dpm_caps |= DPM_CAP_PR_CHECK_PROP(DPM_CAP_PR_CHECK_DISABLE);
    pd_port->dpm_caps |= DPM_CAP_DR_CHECK_PROP(DPM_CAP_DR_CHECK_DISABLE);

    DPM_DBG("dpm_caps = 0x%08x\n", pd_port->dpm_caps);

    src_flag = 0;
    if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER)
        src_flag |= PDO_FIXED_DUAL_ROLE;

    if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA)
        src_flag |= PDO_FIXED_DATA_SWAP;

    if (pd_port->dpm_caps & DPM_CAP_LOCAL_EXT_POWER)
        src_flag |= PDO_FIXED_EXTERNAL;

    if (pd_port->dpm_caps & DPM_CAP_LOCAL_USB_COMM)
        src_flag |= PDO_FIXED_COMM_CAP;

    snk_flag = src_flag;
    if (pd_port->dpm_caps & DPM_CAP_LOCAL_USB_SUSPEND)
        src_flag |= PDO_FIXED_SUSPEND;

    // deprecated
//    if (pd_port->dpm_caps & DPM_CAP_LOCAL_HIGH_CAP)
//        snk_flag |= PDO_FIXED_HIGH_CAP;

    snk_cap->pdos[0] |= snk_flag;
    src_cap->pdos[0] |= src_flag;
}

int pd_core_init(void)
{
    struct pd_port *pd_port = p_pd_port;
    int ret;

    pd_port->pe_pd_state = PE_IDLE2;

    pe_data_init(&pd_port->pe_data);

    ret = pd_parse_pdata(pd_port);
    if (ret)
        return ret;

    pd_core_power_flags_init(pd_port);

    pd_dpm_core_init(pd_port);

    return 0;
}

bool pd_is_reset_cable(struct pd_port *pd_port)
{
    if (!dpm_reaction_check(pd_port, DPM_REACTION_CAP_RESET_CABLE))
        return false;

    return pd_is_cable_communication_available(pd_port);
}

void pd_reset_svid_data(struct pd_port *pd_port)
{
	uint8_t i;
	struct svdm_svid_data *svid_data;

	for (i = 0; i < pd_port->svid_data_cnt; i++) {
		svid_data = &pd_port->svid_data[i];
		svid_data->exist = false;
		svid_data->remote_mode.mode_cnt = 0;
		svid_data->active_mode = 0;
	}
}

void pd_free_unexpected_event(struct pd_port *pd_port)
{
#if CONFIG_USB_PD_DISCARD_AND_UNEXPECT_MSG
	struct pe_data *pe_data = &pd_port->pe_data;

	if (!pe_data->pd_unexpected_event_pending)
		return;

	pe_data->pd_unexpected_event_pending = false;
	__pd_free_event(p_tcpc_dev, &pe_data->pd_unexpected_event);
#endif	/* CONFIG_USB_PD_DISCARD_AND_UNEXPECT_MSG */
}


int pd_reset_protocol_layer(struct pd_port *pd_port, bool sop_only)
{
	struct pe_data *pe_data = &pd_port->pe_data;

	pd_port->state_machine = PE_STATE_MACHINE_NORMAL;

	pd_notify_pe_reset_protocol(pd_port);

#if CONFIG_USB_PD_PE_SOURCE
	pe_data->cap_counter = 0;
#endif	/* CONFIG_USB_PD_PE_SOURCE */

	pe_data->explicit_contract = false;
	pe_data->selected_cap = 0;
	pe_data->during_swap = false;

#if CONFIG_USB_PD_REV30_ALERT_REMOTE
	pe_data->remote_alert = 0;
#endif	/* CONFIG_USB_PD_REV30_ALERT_REMOTE */

#if CONFIG_USB_PD_DFP_FLOW_DELAY_RESET
	if (pe_data->pd_prev_connected)
		dpm_reaction_set(pd_port, DPM_REACTION_DFP_FLOW_DELAY);
#endif	/* CONFIG_USB_PD_DFP_FLOW_DELAY_RESET */

#if CONFIG_USB_PD_DFP_READY_DISCOVER_ID
	dpm_reaction_clear(pd_port, DPM_REACTION_RETURN_VCONN_SRC);
#endif	/* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */

	PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP);

	if (!sop_only) {
		pd_port->request_i = -1;
		pd_port->request_v = TCPC_VBUS_SINK_5V;

		PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP_PRIME);
		PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP_PRIME_PRIME);
	}

#if CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP
	pd_port->msg_id_pr_swap_last = 0xff;
#endif	/* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */

#if CONFIG_USB_PD_DISCARD_AND_UNEXPECT_MSG
	pe_data->pd_sent_ams_init_cmd = true;
	pd_free_unexpected_event(pd_port);
#endif	/* CONFIG_USB_PD_DISCARD_AND_UNEXPECT_MSG */

	return 0;
}


void pd_reset_pe_timer(struct pd_port *pd_port)
{
	tcpc_reset_pe_timer(p_tcpc_dev);

#if CONFIG_USB_PD_REV30_PPS_SINK
	if (pd_port->request_apdo) {
		pd_port->request_apdo = false;
		pd_dpm_start_pps_request(pd_port, false);
	}
#endif	/* CONFIG_USB_PD_REV30_PPS_SINK */
}


int pd_update_connect_state(struct pd_port *pd_port, uint8_t state)
{

	if (pd_port->pd_connect_state == state)
		return 0;

	pd_port->pd_connect_state = state;
	//log_i("%s : pd_state=%d\n",__func__,state);
	return 0;
	//return tcpci_notify_pd_state(p_tcpc_dev, state);
}

int pd_set_rx_enable(struct pd_port *pd_port, uint8_t enable)
{
    if (p_tcpc_dev->ops->set_rx_enable)
        return p_tcpc_dev->ops->set_rx_enable(enable);
    else
        return -1;
}

int pd_enable_vbus_safe0v_detection(struct pd_port *pd_port)
{
	//log_i("WaitVSafe0V\n");
	pd_notify_pe_wait_vbus_once(pd_port, PD_WAIT_VBUS_SAFE0V_ONCE);
	return 0;
}

int pd_enable_vbus_valid_detection(struct pd_port *pd_port, bool wait_valid)
{
	//log_i("WaitVBUS=%d\n", wait_valid);
	pd_notify_pe_wait_vbus_once(pd_port,
		wait_valid ? PD_WAIT_VBUS_VALID_ONCE :
					PD_WAIT_VBUS_INVALID_ONCE);
	return 0;
}

int pd_enable_vbus_stable_detection(struct pd_port *pd_port)
{
	log_i("WaitVStable\n");
	pd_notify_pe_wait_vbus_once(pd_port, PD_WAIT_VBUS_STABLE_ONCE);
	return 0;
}

#if CONFIG_USB_PD_RESET_CABLE
int pd_send_cable_soft_reset(struct pd_port *pd_port)
{
    /* reset_protocol_layer */
    PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP_PRIME);
    dpm_reaction_clear(pd_port, DPM_REACTION_CAP_RESET_CABLE);

    return pd_send_sop_prime_ctrl_msg(PD_CTRL_SOFT_RESET);
}
#endif	/* CONFIG_USB_PD_RESET_CABLE */

int pd_send_soft_reset(struct pd_port *pd_port)
{
	PE_STATE_SEND_SOFT_RESET(pd_port);
	pd_reset_protocol_layer(pd_port, true);
	pd_notify_tcp_event_buf_reset(pd_port, TCP_DPM_RET_DROP_SENT_SRESET);
	pd_update_connect_state(pd_port, PD_CONNECT_SOFT_RESET);

	return pd_send_sop_ctrl_msg(PD_CTRL_SOFT_RESET);
}

static inline int pd_update_msg_header(struct pd_port *pd_port)
{
    return tcpci_set_msg_header(p_tcpc_dev, pd_port->power_role, pd_port->data_role);
}

int pd_set_data_role(struct pd_port *pd_port, uint8_t dr)
{
	int ret = 0;

	if (dr == pd_port->data_role)
		return ret;

	pd_port->data_role = dr;
	ret = pd_update_msg_header(pd_port);
	if (ret < 0)
		return ret;

	//tcpci_notify_role_swap(pd_port->tcpc, TCP_NOTIFY_DR_SWAP, dr);
	return ret;
}


int pd_set_power_role(struct pd_port *pd_port, uint8_t pr)
{
	int ret = 0;

	if (pr == pd_port->power_role)
		return ret;

	pd_port->power_role = pr;
	ret = pd_update_msg_header(pd_port);
	if (ret < 0)
		return ret;

	pd_notify_pe_pr_changed(pd_port);

	//tcpci_notify_role_swap(pd_port->tcpc, TCP_NOTIFY_PR_SWAP, pr);
	return ret;
}

static void pd_init_spec_revision(struct pd_port *pd_port)
{
#if CONFIG_USB_PD_REV30_SYNC_SPEC_REV
	if (p_tcpc_dev->tcpc_flags & TCPC_FLAGS_PD_REV30) {
		//log_i("pd_init_spec_revision PD_REV30\r\n");
		pd_port->pd_revision[0] = PD_REV30;
		pd_port->pd_revision[1] = PD_REV30;
		pd_port->svdm_ver_min[0] = 1;
		pd_port->svdm_ver_min[1] = 1;
	} else {
		pd_port->pd_revision[0] = PD_REV20;
		pd_port->pd_revision[1] = PD_REV20;
		pd_port->svdm_ver_min[0] = 0;
		pd_port->svdm_ver_min[1] = 0;
	}
#endif	/* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */
}

int pd_init_message_hdr(struct pd_port *pd_port, bool act_as_sink)
{
	if (act_as_sink) {
		pd_port->power_role = PD_ROLE_SINK;
		pd_port->data_role = PD_ROLE_UFP;
		pd_port->vconn_role = PD_ROLE_VCONN_OFF;
	} else {
		pd_port->power_role = PD_ROLE_SOURCE;
		pd_port->data_role = PD_ROLE_DFP;
		pd_port->vconn_role = PD_ROLE_VCONN_ON;
	}

	pd_init_spec_revision(pd_port);
	return pd_update_msg_header(pd_port);
}

int pd_set_vconn(struct pd_port *pd_port, uint8_t role)
{
	int ret = 0;
	bool enable = !!(role & PD_ROLE_VCONN_ON);
	//bool en_role = role != PD_ROLE_VCONN_OFF;
	//bool en_role_old = pd_port->vconn_role != PD_ROLE_VCONN_OFF;
	struct sgm41620_tcpc  *tcpc = p_tcpc_dev;

	log_i("%s:%d\n", __func__, role);

#if CONFIG_USB_PD_VCONN_SAFE5V_ONLY
	if (pd_port->pe_data.vconn_highv_prot) {
		log_i("VC_OVER5V\n");
		return -1;
	}
#endif	/* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */

	if (role == pd_port->vconn_role)
		goto out;

	pd_port->vconn_role = role;
	ret = tcpci_set_vconn(tcpc, enable);
	if (ret < 0)
		return ret;

	/*if (en_role != en_role_old)
		tcpci_notify_role_swap(tcpc, TCP_NOTIFY_VCONN_SWAP, en_role);*/
out:
#if CONFIG_USB_PD_VCONN_STABLE_DELAY
	if (role == PD_ROLE_VCONN_DYNAMIC_ON)
		pd_restart_timer(pd_port, PD_TIMER_VCONN_STABLE);
#endif	/* CONFIG_USB_PD_VCONN_STABLE_DELAY */

	if (!enable)
		PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP_PRIME);

	return ret;
}

int pd_reset_local_hw(struct pd_port *pd_port)
{
	uint8_t dr;

	pd_notify_pe_transit_to_default(pd_port);

	pd_reset_pe_timer(pd_port);
	pd_set_rx_enable(pd_port, PD_RX_CAP_PE_HARDRESET);

	pd_port->pe_data.explicit_contract = false;
	pd_port->pe_data.pd_connected = false;
	pd_port->pe_data.pe_ready = false;
    pd_port->pe_data.pd_response_id30_cnt = 0;

#if CONFIG_USB_PD_VCONN_SAFE5V_ONLY
	pd_port->pe_data.vconn_highv_prot = false;
	pd_port->pe_data.vconn_highv_prot_role = PD_ROLE_VCONN_OFF;
#endif	/* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */

#if CONFIG_USB_PD_RESET_CABLE
	dpm_reaction_clear(pd_port, DPM_REACTION_CAP_RESET_CABLE);
#endif	/* CONFIG_USB_PD_RESET_CABLE */

	//pd_reset_modal_operation(pd_port);

	pd_set_vconn(pd_port, PD_ROLE_VCONN_OFF);

	if (pd_port->power_role == PD_ROLE_SINK)
		dr = PD_ROLE_UFP;
	else
		dr = PD_ROLE_DFP;

	pd_port->state_machine = PE_STATE_MACHINE_NORMAL;

	pd_set_data_role(pd_port, dr);
	pd_init_spec_revision(pd_port);
	pd_dpm_notify_pe_hardreset(pd_port);
	//log_i("reset_local_hw\n");

	return 0;
}

void pd_extract_rdo_power(uint32_t rdo, uint32_t pdo,
			uint32_t *op_curr, uint32_t *max_curr)
{
	uint32_t op_power, max_power, vmin;

	switch (pdo & PDO_TYPE_MASK) {
	case PDO_TYPE_FIXED:
	case PDO_TYPE_VARIABLE:
		*op_curr = RDO_FIXED_VAR_EXTRACT_OP_CURR(rdo);
		*max_curr = RDO_FIXED_VAR_EXTRACT_MAX_CURR(rdo);
		break;

	case PDO_TYPE_BATTERY: /* TODO: check it later !! */
		//vmin = PDO_BATT_EXTRACT_MIN_VOLT(pdo);
		op_power = RDO_BATT_EXTRACT_OP_POWER(rdo);
		max_power = RDO_BATT_EXTRACT_MAX_POWER(rdo);

		*op_curr = op_power / vmin;
		*max_curr = max_power / vmin;
		break;

#if CONFIG_USB_PD_REV30_PPS_SOURCE
	case PDO_TYPE_APDO:
		*op_curr = RDO_APDO_EXTRACT_OP_MA(rdo);
		*max_curr = RDO_APDO_EXTRACT_OP_MA(rdo);
		break;
#endif	/* CONFIG_USB_PD_REV30_PPS_SOURCE */

	default:
		*op_curr = *max_curr = 0;
		break;
	}
}

uint32_t pd_reset_pdo_power(struct sgm41620_tcpc *tcpc,
			uint32_t pdo, uint32_t imax)
{
	uint32_t ioper;

	switch (pdo & PDO_TYPE_MASK) {
	case PDO_TYPE_FIXED:
		ioper = PDO_FIXED_EXTRACT_CURR(pdo);
		if (ioper > imax)
			return PDO_FIXED_RESET_CURR(pdo, imax);
		break;

	case PDO_TYPE_VARIABLE:
		ioper = PDO_VAR_EXTRACT_CURR(pdo);
		if (ioper > imax)
			return PDO_VAR_RESET_CURR(pdo, imax);
		break;

	case PDO_TYPE_BATTERY:
		/* TODO: check it later !! */
		log_i("No Support\n");
		break;

#if CONFIG_USB_PD_REV30_PPS_SOURCE
	case PDO_TYPE_APDO:
		/* TODO: check it later !! */
		break;
#endif	/* CONFIG_USB_PD_REV30_PPS_SOURCE */
	}
	return pdo;
}

static inline uint32_t pd_get_cable_curr_lvl(struct pd_port *pd_port)
{
	return PD_VDO_CABLE_CURR(pd_port->pe_data.cable_vdos[VDO_DISCOVER_ID_CABLE]);
}

uint32_t pd_get_cable_current_limit(struct pd_port *pd_port)
{
	switch (pd_get_cable_curr_lvl(pd_port)) {
	case CABLE_CURR_5A:
		return 5000;
	default:
		return 3000;
	}
}

int pd_send_hard_reset(struct pd_port *pd_port)
{

	PE_STATE_HRESET_IF_TX_FAILED(pd_port);

	pd_port->pe_data.hard_reset_counter++;
	pd_notify_pe_send_hard_reset(pd_port);

	return tcpci_transmit(p_tcpc_dev, (enum tcpm_transmit_type)TCPC_TX_HARD_RESET, 0, NULL);
}

int pd_send_bist_mode2(struct pd_port *pd_port)
{
	int ret = 0;
//	struct sgm41620_tcpc *tcpc = p_tcpc_dev;

	pd_notify_tcp_event_buf_reset(pd_port, TCP_DPM_RET_DROP_SEND_BIST);

#if CONFIG_USB_PD_TRANSMIT_BIST2
	log_i("BIST_MODE_2\n");
	tcpci_transmit(p_tcpc_dev, (enum tcpm_transmit_type)TCPC_TX_BIST_MODE_2, 0, NULL);
	//ret = tcpci_transmit(tcpc, TCPC_TX_BIST_MODE_2, 0, NULL);
#else
	ret = tcpci_set_bist_carrier_mode(tcpc, 1 << 2);
#endif

	return ret;
}

int pd_disable_bist_mode2(struct pd_port *pd_port)
{
#if !CONFIG_USB_PD_TRANSMIT_BIST2
	return tcpci_set_bist_carrier_mode(
		pd_port->tcpc, 0);
#else
	return 0;
#endif
}






/* ---- Send / Reply VDM Command ----*/

uint8_t pd_get_svdm_ver_min(
	struct pd_port *pd_port, enum tcpm_transmit_type sop_type)
{
#if CONFIG_USB_PD_REV30_SYNC_SPEC_REV
	if (sop_type >= ARRAY_SIZE(pd_port->svdm_ver_min))
		return 0;
	return pd_port->svdm_ver_min[sop_type];
#else
	return 0;
#endif	/* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */
}

int pd_send_svdm_request(struct pd_port *pd_port,
		uint8_t sop_type, uint16_t svid, uint8_t vdm_cmd,
		uint8_t obj_pos, uint8_t cnt, uint32_t *data_obj,
		uint32_t timer_id)
{
	int ret;
	uint8_t ver = SVDM_REV10;
	uint32_t payload[PD_DATA_OBJ_SIZE];

	if (cnt > VDO_MAX_NR) {
		log_i("cnt > VDO_MAX_NR\r\n");
		return -1;
	}

	if (pd_get_rev(pd_port, sop_type) >= PD_REV30)
		ver = SVDM_REV20;

	payload[0] = VDO_S(svid, ver, pd_get_svdm_ver_min(pd_port, (enum tcpm_transmit_type)sop_type),
	                   CMDT_INIT,vdm_cmd, obj_pos);
	memcpy(&payload[1], data_obj, sizeof(uint32_t) * cnt);

	ret = pd_send_data_msg(sop_type, PD_DATA_VENDOR_DEF, 1+cnt, payload);

	if (ret == 0 && timer_id != 0)
		VDM_STATE_RESPONSE_CMD(pd_port, timer_id);

	return ret;
}

int pd_send_vdm_discover_id(struct pd_port *pd_port, uint8_t sop_type)
{
	return pd_send_svdm_request(pd_port, sop_type, USB_SID_PD,
		CMD_DISCOVER_IDENT, 0, 0, NULL, PD_TIMER_VDM_RESPONSE);
}

int pd_send_vdm_discover_svids(struct pd_port *pd_port, uint8_t sop_type)
{
	return pd_send_svdm_request(pd_port, sop_type, USB_SID_PD,
		CMD_DISCOVER_SVIDS, 0, 0, NULL, PD_TIMER_VDM_RESPONSE);
}

int pd_send_vdm_discover_modes(struct pd_port *pd_port, uint8_t sop_type, uint16_t svid)
{
	return pd_send_svdm_request(pd_port, sop_type, svid,
		CMD_DISCOVER_MODES, 0, 0, NULL, PD_TIMER_VDM_RESPONSE);
}

int pd_send_vdm_enter_mode(struct pd_port *pd_port,uint8_t sop_type, uint16_t svid, uint8_t obj_pos)
{
	return pd_send_svdm_request(pd_port, sop_type, svid,
		CMD_ENTER_MODE, obj_pos, 0, NULL, PD_TIMER_VDM_MODE_ENTRY);
}

int pd_send_vdm_exit_mode(struct pd_port *pd_port, uint8_t sop_type, uint16_t svid, uint8_t obj_pos)
{
	return pd_send_svdm_request(pd_port, sop_type, svid,
		CMD_EXIT_MODE, obj_pos, 0, NULL, PD_TIMER_VDM_MODE_EXIT);
}

int pd_send_vdm_attention(struct pd_port *pd_port,uint8_t sop_type, uint16_t svid, uint8_t obj_pos)
{
	return pd_send_svdm_request(pd_port, sop_type, svid,
		CMD_ATTENTION, obj_pos, 0, NULL, 0);
}

int pd_send_vdm_dp_attention(struct pd_port *pd_port,uint8_t sop_type, uint8_t obj_pos, uint32_t dp_status)
{
	return pd_send_svdm_request(pd_port, sop_type, USB_SID_DISPLAYPORT,
		CMD_ATTENTION, obj_pos, 1, &dp_status, 0);
}

int pd_send_vdm_dp_status(struct pd_port *pd_port, uint8_t sop_type, uint8_t obj_pos, uint8_t cnt, uint32_t *data_obj)
{
	return pd_send_svdm_request(pd_port, sop_type, USB_SID_DISPLAYPORT,
		CMD_DP_STATUS, obj_pos, cnt, data_obj, PD_TIMER_VDM_RESPONSE);
}

int pd_send_vdm_dp_config(struct pd_port *pd_port, uint8_t sop_type, uint8_t obj_pos, uint8_t cnt, uint32_t *data_obj)
{
	return pd_send_svdm_request(pd_port, sop_type, USB_SID_DISPLAYPORT,
		CMD_DP_CONFIG, obj_pos, cnt, data_obj, PD_TIMER_VDM_RESPONSE);
}

int pd_reply_svdm_request_simply(struct pd_port *pd_port, uint8_t reply)
{
	return pd_reply_svdm_request(pd_port, reply, 0, NULL);
}

int pd_reply_wait_reject_msg(struct pd_port *pd_port)
{
	PE_STATE_WAIT_TX_SUCCESS(pd_port);
	return pd_reply_wait_reject_msg_no_resp(pd_port);
}


int pd_reply_wait_reject_msg_no_resp(struct pd_port *pd_port)
{
	struct pd_event *pd_event = pd_get_curr_pd_event(pd_port);
	uint8_t msg = pd_event->msg_sec == PD_DPM_NAK_REJECT ?
		PD_CTRL_REJECT : PD_CTRL_WAIT;

	return pd_send_sop_ctrl_msg(msg);
}

#if CONFIG_USB_PD_REV30
int pd_send_ext_msg(struct pd_port *pd_port,
		uint8_t sop_type, uint8_t msg, bool request,
		uint8_t chunk_nr, uint8_t size, const uint8_t *data)
{
	uint8_t cnt;
	uint32_t payload[PD_DATA_OBJ_SIZE];

	uint16_t *ext_hdr = (uint16_t *)payload;

	cnt = ((size + PD_EXT_HEADER_PAYLOAD_INDEX - 1) / 4) + 1;
	payload[cnt-1] = 0;		/* Padding Byte should be 0 */

	*ext_hdr = PD_EXT_HEADER_CK(size, request, chunk_nr, true);
	memcpy(&ext_hdr[1], data, size);

	return pd_send_message(sop_type, msg, true, cnt, payload);
}

int pd_send_sop_ext_msg(struct pd_port *pd_port,
	uint8_t msg, uint8_t size, const void *data)
{
	/* TODO: chunking */

	return pd_send_ext_msg(pd_port,
		TCPC_TX_SOP, msg, false, 0, size, data);
}
#endif	/* CONFIG_USB_PD_REV30 */


int pd_reply_svdm_request(struct pd_port *pd_port,
	uint8_t reply, uint8_t cnt, uint32_t *data_obj)
{
	uint8_t ver = SVDM_REV10;
	uint32_t payload[PD_DATA_OBJ_SIZE];

	if (cnt > VDO_MAX_NR) {
		log_i("cnt > VDO_MAX_NR\r\n");
		return -1;
	}

	if (pd_check_rev30(pd_port))
		ver = SVDM_REV20;

	payload[0] = VDO_REPLY(ver, pd_get_svdm_ver_min(pd_port, TCPC_TX_SOP),
			       reply, pd_get_msg_vdm_hdr(pd_port));

	if (cnt > 0 && cnt <= PD_DATA_OBJ_SIZE - 1) {
		memcpy(&payload[1], data_obj, sizeof(uint32_t) * cnt);
	}

	if (reply != CMDT_RSP_ACK){
		log_i("VDM_NAK_BUSY\n");
	}
	else{
		//log_i("VDM_ACK\n");
	}

	VDM_STATE_REPLY_SVDM_REQUEST(pd_port);

	return pd_send_sop_data_msg(PD_DATA_VENDOR_DEF, 1+cnt, payload);
}

int pd_enable_bist_test_mode(struct pd_port *pd_port, bool en)
{
	struct sgm41620_tcpc *tcpc = p_tcpc_dev;
	log_i("bist_test_mode=%d\n", en);
	return tcpci_set_bist_test_mode(tcpc, en);
}

/* ---- Handle PD Message ----*/

int pd_handle_soft_reset(struct pd_port *pd_port)
{
	PE_STATE_RECV_SOFT_RESET(pd_port);

	pd_reset_protocol_layer(pd_port, true);
	pd_notify_tcp_event_buf_reset(pd_port, TCP_DPM_RET_DROP_RECV_SRESET);
	pd_update_connect_state(pd_port, PD_CONNECT_SOFT_RESET);
	return pd_send_sop_ctrl_msg(PD_CTRL_ACCEPT);
}


void pd_handle_first_pd_command(struct pd_port *pd_port)
{
#if CONFIG_USB_PD_REV30_SYNC_SPEC_REV
	pd_sync_sop_spec_revision(pd_port);
#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */

	pd_port->pe_data.pd_connected = true;
	pd_port->pe_data.pd_prev_connected = true;

	//tcpci_notify_pd_mode(pd_port->tcpc);
}

void pd_handle_hard_reset_recovery(struct pd_port *pd_port)
{
	/* Stop NoResponseTimer and reset HardResetCounter to zero */
	pd_port->pe_data.hard_reset_counter = 0;
	pd_disable_timer(pd_port, PD_TIMER_NO_RESPONSE);

#if CONFIG_USB_PD_RENEGOTIATION_COUNTER
	pd_port->pe_data.renegotiation_count++;
#endif	/* CONFIG_USB_PD_RENEGOTIATION_COUNTER */

#if CONFIG_USB_PD_RECV_HRESET_COUNTER
	pd_port->pe_data.recv_hard_reset_count = 0;
#endif	/* CONFIG_USB_PD_RECV_HRESET_COUNTER */
	//log_i("pd_handle_hard_reset_recovery\r\n");
	pd_notify_pe_hard_reset_completed(pd_port);
}

int pd_send_message(uint8_t sop_type,
		uint8_t msg, bool ext, uint16_t count, const uint32_t *data)
{
	int ret;
	uint8_t msg_id;
	uint16_t msg_hdr;
	uint16_t msg_hdr_private;
	uint8_t pd_rev = pd_get_rev(p_pd_port, sop_type);
	uint8_t type = PD_TX_STATE_WAIT_CRC_PD;
	struct pe_data *pe_data = &p_pd_port->pe_data;

	if (p_tcpc_dev->typec_attach_old == 0) {
		log_i("[SendMsg] Unattached\n");
		return 0;
	}

	if (p_tcpc_dev->pd_hard_reset_event_pending) {
		log_i("[SendMsg] HardReset Pending");
		return 0;
	}

	if (sop_type == TCPC_TX_SOP)
		msg_hdr_private = PD_HEADER_ROLE(
			p_pd_port->power_role, p_pd_port->data_role);
	else
		msg_hdr_private = 0;

	if (pd_rev >= PD_REV30)
		p_tcpc_dev->pd_retry_count = PD30_RETRY_COUNT;
	else
		p_tcpc_dev->pd_retry_count = PD_RETRY_COUNT;

	msg_id = pe_data->msg_id_tx[sop_type];
	msg_hdr = PD_HEADER_COMMON(
		msg, pd_rev, msg_id, count, ext, msg_hdr_private);

	if (count > 0 && !ext && msg == PD_DATA_VENDOR_DEF)
		type = PD_TX_STATE_WAIT_CRC_VDM;

	pe_data->msg_id_tx[sop_type] = (msg_id+1) % PD_MSG_ID_MAX;

	pd_notify_pe_transmit_msg(p_pd_port, type);
	/* ext-cmd 15 is reserved */
	//log_i("msg_id = %d, msg_hdr = 0x%x,count = %d\n",msg_id,msg_hdr,count);
	ret = tcpci_transmit(p_tcpc_dev, (enum tcpm_transmit_type)sop_type, msg_hdr, data);
	if (ret)
		log_e("[SendMsg] Failed, %d\r\n", ret);

	return ret;
}

int pd_send_data_msg(uint8_t sop_type, uint8_t msg, uint8_t cnt, uint32_t *payload)
{
	return pd_send_message(sop_type, msg, false, cnt, payload);
}

int pd_send_sop_ctrl_msg(uint8_t msg)
{
	return pd_send_message(TCPC_TX_SOP, msg, false, 0, NULL);
}

int pd_send_sop_prime_ctrl_msg(uint8_t msg)
{
	return pd_send_message(TCPC_TX_SOP_PRIME, msg, false, 0, NULL);
}

int pd_send_sop_prime_prime_ctrl_msg(uint8_t msg)
{
	return pd_send_message(TCPC_TX_SOP_PRIME_PRIME, msg, false, 0, NULL);
}

int pd_send_sop_data_msg(uint8_t msg, uint8_t cnt, const uint32_t *payload)
{
	return pd_send_message(TCPC_TX_SOP, msg, false, cnt, payload);
}

#if CONFIG_USB_PD_REV30
void pd_set_sink_tx(struct pd_port *pd_port, uint8_t cc)
{
	if (cc == PD30_SINK_TX_OK &&
		pd_port->pe_data.pd_traffic_control != PD_SINK_TX_OK) {
		DPM_DBG("set SinkTxOk\n");
		tcpci_set_cc(p_tcpc_dev, cc);
		pd_port->pe_data.pd_traffic_control = PD_SINK_TX_OK;
		pd_disable_timer(pd_port, PD_TIMER_SINK_TX);
	} else if (cc == PD30_SINK_TX_NG &&
		pd_port->pe_data.pd_traffic_control == PD_SINK_TX_OK) {
		DPM_DBG("set SinkTxNG\n");
		tcpci_set_cc(p_tcpc_dev, cc);
		pd_port->pe_data.pd_traffic_control = PD_SINK_TX_NG;
		pd_enable_timer(pd_port, PD_TIMER_SINK_TX);
	}
}

#if CONFIG_USB_PD_REV30_SYNC_SPEC_REV

void pd_sync_svdm_ver_min(
	struct pd_port *pd_port, enum tcpm_transmit_type sop_type, uint8_t ver)
{

	if (sop_type >= ARRAY_SIZE(pd_port->svdm_ver_min))
		return;

	pd_port->svdm_ver_min[sop_type] =
		MIN(pd_port->svdm_ver_min[sop_type], ver);
	//log_i("svdm_ver_min[%d]=%u\n", sop_type, ver);
}



void pd_sync_sop_spec_revision(struct pd_port *pd_port)
{
	uint8_t rev = pd_get_msg_hdr_rev(pd_port);
	uint8_t ver_min = rev >= PD_REV30 ? 1 : 0;

	if (!pd_port->pe_data.pd_connected) {
		pd_port->pd_revision[0] = MIN(pd_port->pd_revision[0], rev);
		pd_port->pd_revision[1] = MIN(pd_port->pd_revision[1], rev);
		pd_sync_svdm_ver_min(pd_port, TCPC_TX_SOP, ver_min);
		pd_sync_svdm_ver_min(pd_port, TCPC_TX_SOP_PRIME, ver_min);
		//log_i("pd_rev=%d\n", pd_port->pd_revision[0]);
	}
}

void pd_sync_sop_prime_spec_revision(struct pd_port *pd_port, uint8_t rev)
{
	uint8_t ver_min = rev >= PD_REV30 ? 1 : 0;
	struct pe_data *pe_data = &pd_port->pe_data;

	if (!pe_data->cable_rev_discovered) {
		pe_data->cable_rev_discovered = true;
		pd_port->pd_revision[1] = MIN(pd_port->pd_revision[1], rev);
		pd_sync_svdm_ver_min(pd_port, TCPC_TX_SOP_PRIME, ver_min);
		log_i("cable_rev=%d\n", pd_port->pd_revision[1]);
	}
}

#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */


bool pd_is_multi_chunk_msg(struct pd_port *pd_port)
{
	uint16_t size;

	if (pd_get_msg_hdr_ext(pd_port)) {
		size = pd_get_msg_data_size(pd_port);
		if (size > MAX_EXTENDED_MSG_CHUNK_LEN) {
			log_i("multi_chunk_msg = TRUE (%d)\n", size);
			return true;
		}
	}

	return false;
}

static uint8_t pd_get_fix_battery_nr(struct pd_port *pd_port)
{
    return PD_SCEDB_FIX_BAT_NR(pd_port->bat_nr);
}

struct pd_battery_info *pd_get_battery_info(
	struct pd_port *pd_port, enum pd_battery_reference ref)
{
#if CONFIG_USB_PD_REV30_BAT_INFO
	if (ref < pd_get_fix_battery_nr(pd_port))
		return &pd_port->fix_bat_info[ref];
#endif	/* CONFIG_USB_PD_REV30_BAT_INFO */

	/* TODO: for swap battery */
	return NULL;
}


#endif	/* ndef CONFIG_USB_PD_REV30 */

void pd_noitfy_pe_bist_mode(struct pd_port *pd_port, uint8_t mode)
{
	p_tcpc_dev->pd_bist_mode = mode;
}

void pe_data_init(struct pe_data *pe_data)
{
	pe_data->pe_state_timer = PD_TIMER_NR;
	pe_data->vdm_state_timer = PD_TIMER_NR;
}

/* ---- pd_timer ---- */
void pd_restart_timer(struct pd_port *pd_port, uint32_t timer_id)
{
	tcpc_restart_timer(p_tcpc_dev, timer_id);
}

void pd_enable_timer(struct pd_port *pd_port, uint32_t timer_id)
{
	tcpc_enable_timer(p_tcpc_dev, timer_id);
}

void pd_enable_pe_state_timer(struct pd_port *pd_port, uint32_t timer_id)
{
	pd_port->pe_data.pe_state_timer = timer_id;
	pd_enable_timer(pd_port, timer_id);
}

void pd_disable_timer(struct pd_port *pd_port, uint32_t timer_id)
{
	tcpc_disable_timer(p_tcpc_dev, timer_id);
}

void pd_disable_pe_state_timer(struct pd_port *pd_port)
{
	struct pe_data *pe_data = &pd_port->pe_data;

	if (pe_data->pe_state_timer >= PD_TIMER_NR)
		return;

	pd_disable_timer(pd_port, pe_data->pe_state_timer);
	pe_data->pe_state_timer = PD_TIMER_NR;
}

